package com.sk.util;

import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import org.springframework.core.ResolvableType;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 对象操作/转换工具。
 * <p>
 * SpringBeanUtils补充
 *
 * @author smy
 */
public class ObjectUtil {

    /**
     * 获取第一个非空对象
     *
     * @param ts  对象数组
     * @param <T> 泛型越是
     * @return 第一个非空对象
     */
    public static <T> T getNotNull(T... ts) {
        for (T t : ts) {
            if (t != null) {
                return t;
            }
        }
        return null;
    }

    public static <T> List<T> array2List(T... ts) {
        return ts.length == 0 ? Collections.emptyList() : Arrays.asList(ts);
    }

    public static <T> boolean arrayContains(T value, T... ts) {
        return Arrays.asList(ts).contains(value);
    }


    public static void bean2bean(Object source, Object target) {
        bean2bean(source, target, ((field, o) -> Objects.nonNull(o)), field -> hasField(target.getClass(), field.getName()));
    }

    public static void bean2bean(Object source, Object target, BiPredicate<Field, Object> predicate, Function<Field, Field> fieldFunction) {
        List<Field> fields = getFields(source.getClass());
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = getValue(source, field);
            Field field2 = fieldFunction.apply(field);
            if (predicate.test(field, value) && field2 != null) {
                field2.setAccessible(true);
                ObjectUtil.setValue(target, field, value);
            }
        }
    }

    public static JSONObject bean2json(Object source){
        return  (JSONObject) JSONObject.toJSON(source);
    }


    @SneakyThrows
    public static <T> T newInstance(Class<T> c) {
        return c.getDeclaredConstructor().newInstance();
    }

    @SneakyThrows
    public static <T> Constructor<T> getConstructor(Class<T> c, Class<?>... pTypes) {
        return c.getConstructor(pTypes);
    }

    @SneakyThrows
    public static <T> List<T> newInstance(Constructor<T> c, List<Object[]> objectList) {
        if (objectList == null) {
            return null;
        }
        if (objectList.isEmpty()) {
            return new ArrayList<>();
        }
        List<T> list = new ArrayList<>();
        for (Object[] objects : objectList) {
            list.add(c.newInstance(objects));
        }
        return list;
    }


    @SneakyThrows
    public static <T> T newInstance(Constructor<T> c, Object... args) {
        return c.newInstance(args);
    }

    public static String[] getFieldNames(Class<?> c) {
        return getFields(c).stream().map(Field::getName).toArray(String[]::new);
    }

    public static List<Field> getFields(Class<?> c) {
        return getFields(c, f -> true);
    }

    public static List<Field> getFields(Class<?> c, Predicate<Field> predicate) {
        LinkedList<Field> list = new LinkedList<>();
        Field[] fields;
        Field field;
        while (c != Object.class) {
            fields = c.getDeclaredFields();
            for (int i = fields.length - 1; i >= 0; i--) {
                field = fields[i];
                if (predicate.test(field)) {
                    list.addFirst(field);
                }
            }
            c = c.getSuperclass();
        }
        return list;
    }

    public static Field getField(Class<?> c, String fieldName) {
        Class<?> t = c;
        while (!Object.class.equals(t)) {
            try {
                return t.getDeclaredField(fieldName);
            } catch (Exception e) {
                t = t.getSuperclass();
            }
        }
        throw new RuntimeException(new NoSuchFieldException(c + "." + fieldName));
    }

    public static Field hasField(Class<?> c, String fieldName) {
        try {
            return getField(c, fieldName);
        } catch (Exception e) {
            return null;
        }
    }

    @SneakyThrows
    @SuppressWarnings("unchecked")
    public static <T> T getValue(Object object, String fieldName) {
        Field field = getField(object.getClass(), fieldName);
        field.setAccessible(true);
        return (T) field.get(object);
    }

    @SneakyThrows
    @SuppressWarnings("unchecked")
    public static <T> T getValue(Object object, Field field) {
        return (T) field.get(object);
    }

    @SneakyThrows
    public static void setValue(Object object, Field field, Object value) {
        field.set(object, value);
    }

    @SneakyThrows
    public static void setValue(Object object, String fieldName, Object value) {
        Field field = getField(object.getClass(), fieldName);
        field.setAccessible(true);
        field.set(object, value);
    }

    public static Method getMethodByName(Class<?> v, String methodName) {
        for (Method method : v.getMethods()) {
            if (methodName.equals(method.getName())) {
                return method;
            }
        }
        throw new RuntimeException("没有找到方法:" + v.getName() + "." + methodName);
    }

    @SneakyThrows
    public static <T> T method(Object object, String methodName, Object... args) {
        Class<?> c = object.getClass();
        Class<?>[] parameterTypes = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            parameterTypes[i] = args[i].getClass();
        }
        Method m = c.getMethod(methodName, parameterTypes);
        return method(object, m, args);
    }


    @SneakyThrows
    @SuppressWarnings("unchecked")
    public static <T> T method(Object object, Method method, Object... args) {
        return (T) method.invoke(object, args);
    }

    public static Class getGeneric(Class clazz) {
        return getGeneric(clazz, 0);
    }

    public static Class getGeneric(Class clazz, int num) {
        //获取泛型考虑优先使用Spring的ResolvableType
        return ResolvableType.forClass(clazz).getSuperType().getGeneric(num).resolve();
    }

}
